#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sstream>
#include <boost/filesystem.hpp>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "bin2obj.h"

// https://blog.csdn.net/u013467442/article/details/50097821/

typedef struct
{
    std::string signature;
    unsigned char header_bytes;
    unsigned char vertex_coordinate_bytes;
    unsigned char normal_coordinate_bytes;
    unsigned char uv_coordinate_bytes;

    unsigned char vertex_index_bytes;
    unsigned char normal_index_bytes;
    unsigned char uv_index_bytes;
    unsigned char material_index_bytes;

    unsigned int nvertices;
    unsigned int nnormals;
    unsigned int nuvs;

    unsigned int ntri_flat;
    unsigned int ntri_smooth;
    unsigned int ntri_flat_uv;
    unsigned int ntri_smooth_uv;

    unsigned int nquad_flat;
    unsigned int nquad_smooth;
    unsigned int nquad_flat_uv;
    unsigned int nquad_smooth_uv;
} MetaData;

JsonFile jsonFile;
FILE *fp, *tp;
unsigned char *data;
MetaData md;

std::string parseString(unsigned char *data, unsigned int offset, unsigned int length)
{
    std::ostringstream out;

    for (int i = 0; i < length; i++)
    {
        out << data[offset + i];
    }

    return out.str();
}

unsigned char parseUChar8(unsigned char *data, unsigned int offset)
{
    return data[offset];
}

char parseChar8(unsigned char *data, unsigned int offset)
{
    return (char)data[offset];
}

uint32_t parseUInt32(unsigned char *data, unsigned int offset)
{
    return (data[offset + 3] << 24) | (data[offset + 2] << 16) | (data[offset + 1] << 8) | data[offset];
}

float parseFloat32(unsigned char *data, unsigned int offset)
{
    union
    {
        float f;
        unsigned char b[4];
    } u;

    u.b[3] = data[offset + 3];
    u.b[2] = data[offset + 2];
    u.b[1] = data[offset + 1];
    u.b[0] = data[offset + 0];

    return u.f;
}

uint16_t parseUShort16(unsigned char *data, unsigned int offset)
{
    return (data[offset + 1] << 8) | data[offset];
}

MetaData parseMetaData(unsigned char *data, unsigned int offset)
{
    MetaData metaData = {
        signature : parseString(data, offset, 12),
        header_bytes : parseUChar8(data, offset + 12),

        vertex_coordinate_bytes : parseUChar8(data, offset + 13),
        normal_coordinate_bytes : parseUChar8(data, offset + 14),
        uv_coordinate_bytes : parseUChar8(data, offset + 15),

        vertex_index_bytes : parseUChar8(data, offset + 16),
        normal_index_bytes : parseUChar8(data, offset + 17),
        uv_index_bytes : parseUChar8(data, offset + 18),
        material_index_bytes : parseUChar8(data, offset + 19),

        nvertices : parseUInt32(data, offset + 20),
        nnormals : parseUInt32(data, offset + 20 + 4 * 1),
        nuvs : parseUInt32(data, offset + 20 + 4 * 2),

        ntri_flat : parseUInt32(data, offset + 20 + 4 * 3),
        ntri_smooth : parseUInt32(data, offset + 20 + 4 * 4),
        ntri_flat_uv : parseUInt32(data, offset + 20 + 4 * 5),
        ntri_smooth_uv : parseUInt32(data, offset + 20 + 4 * 6),

        nquad_flat : parseUInt32(data, offset + 20 + 4 * 7),
        nquad_smooth : parseUInt32(data, offset + 20 + 4 * 8),
        nquad_flat_uv : parseUInt32(data, offset + 20 + 4 * 9),
        nquad_smooth_uv : parseUInt32(data, offset + 20 + 4 * 10)
    };

    return metaData;
}

inline unsigned int handlePadding(unsigned int n)
{
    return (n % 4) ? (4 - n % 4) : 0;
}

unsigned int init_vertices(unsigned int start)
{
    unsigned int nElements = md.nvertices;

    // 1. vertices
    // ------------
    // x float   4
    // y float   4
    // z float   4
    for (unsigned int i = 0; i < nElements; i++)
    {
        float x, y, z;

        x = parseFloat32(data, start + i * 3 * sizeof(float));
        y = parseFloat32(data, start + (i * 3 + 1) * sizeof(float));
        z = parseFloat32(data, start + (i * 3 + 2) * sizeof(float));

        fprintf(tp, "v %f %f %f\n", x, y, z);
    }

    fprintf(stderr, "Processed %d vertices [%d]\n", nElements, start);

    return nElements * 3 * sizeof(float);
}

unsigned int init_normals(unsigned int start)
{
    unsigned int nElements = md.nnormals;

    for (unsigned int i = 0; i < nElements; i++)
    {
        char x = parseChar8(data, start + i * 3 * sizeof(char));
        char y = parseChar8(data, start + (i * 3 + 1) * sizeof(char));
        char z = parseChar8(data, start + (i * 3 + 2) * sizeof(char));

        fprintf(tp, "vn %f %f %f\n", x / 127.0, y / 127.0, z / 127.0);
    }

    fprintf(stderr, "Processed %d normals [%d]\n", nElements, start);

    return nElements * 3 * sizeof(char);
}

unsigned int init_uvs(unsigned int start)
{
    unsigned int nElements = md.nuvs;

    for (unsigned int i = 0; i < nElements; i++)
    {
        float u = parseFloat32(data, start + i * 2 * sizeof(float));
        float v = parseFloat32(data, start + (i * 2 + 1) * sizeof(float));

        fprintf(tp, "vt %f %f\n", u, v);
    }

    fprintf(stderr, "Processed %d uvs [%d]\n", nElements, start);

    return nElements * 2 * sizeof(float);
}

uint16_t cur_m = 65535;

void use_material(const uint16_t index)
{
    if (index == cur_m)
    {
        return;
    }
    if (index < jsonFile.Materials.size())
    {
        fprintf(tp, "usemtl %s\n", jsonFile.Materials[index].DbgName.c_str());
    }
    else if (jsonFile.Materials.size() > 0)
    {
        fprintf(tp, "usemtl %s\n", jsonFile.Materials[0].DbgName.c_str());
    }
    else
    {
        fprintf(tp, "usemtl defmtl\n");
    }
    cur_m = index;
}

void init_faces3_flat(unsigned int nElements, unsigned int offsetVertices, unsigned int offsetMaterials)
{
    for (unsigned int i = 0; i < md.ntri_flat; i++)
    {
        uint32_t a = parseUInt32(data, offsetVertices + (i * 3) * sizeof(uint32_t));
        uint32_t b = parseUInt32(data, offsetVertices + (i * 3 + 1) * sizeof(uint32_t));
        uint32_t c = parseUInt32(data, offsetVertices + (i * 3 + 2) * sizeof(uint32_t));

        uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));

        use_material(m);
        fprintf(tp, "f %d %d %d\n", a + 1, b + 1, c + 1);
    }
}

void init_uvs3(unsigned int nElements, unsigned int offset, unsigned int offsetMaterials)
{
    for (unsigned int i = 0; i < nElements; i++)
    {
        uint32_t a = parseUInt32(data, offset + i * 3 * sizeof(uint32_t));
        uint32_t b = parseUInt32(data, offset + (i * 3 + 1) * sizeof(uint32_t));
        uint32_t c = parseUInt32(data, offset + (i * 3 + 2) * sizeof(uint32_t));

        uint32_t ua = parseUInt32(data, offset + (md.ntri_flat_uv * 12) + i * 3 * sizeof(uint32_t));
        uint32_t ub = parseUInt32(data, offset + (md.ntri_flat_uv * 12) + (i * 3 + 1) * sizeof(uint32_t));
        uint32_t uc = parseUInt32(data, offset + (md.ntri_flat_uv * 12) + (i * 3 + 2) * sizeof(uint32_t));

        uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
        use_material(m);

        fprintf(tp, "f %d/%d %d/%d %d/%d\n", a + 1, ua + 1, b + 1, ub + 1, c + 1, uc + 1);
    }

    fprintf(stderr, "Processed %d tri flat uvs [%d]\n", md.ntri_flat_uv, offset);
}

void init_triangles_flat_uv(unsigned int start)
{
    unsigned int nElements = md.ntri_flat_uv;

    if (nElements > 0)
    {
        unsigned int offsetUvs = start + nElements * sizeof(uint32_t) * 3;
        unsigned int offsetMaterials = offsetUvs + nElements * sizeof(uint32_t) * 3;

        init_faces3_flat(nElements, start, offsetMaterials);
        init_uvs3(nElements, offsetUvs, offsetMaterials);
    }
}

void init_faces3_smooth(unsigned int nElements, unsigned int offsetVertices, unsigned int offsetNormals, unsigned int offsetMaterials)
{
    for (unsigned int i = 0; i < nElements; i++)
    {
        uint32_t a = parseUInt32(data, offsetVertices + (i * 3) * sizeof(uint32_t));
        uint32_t b = parseUInt32(data, offsetVertices + (i * 3 + 1) * sizeof(uint32_t));
        uint32_t c = parseUInt32(data, offsetVertices + (i * 3 + 2) * sizeof(uint32_t));

        uint32_t na = parseUInt32(data, offsetNormals + (i * 3) * sizeof(uint32_t));
        uint32_t nb = parseUInt32(data, offsetNormals + (i * 3 + 1) * sizeof(uint32_t));
        uint32_t nc = parseUInt32(data, offsetNormals + (i * 3 + 2) * sizeof(uint32_t));

        uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
        use_material(m);

        fprintf(tp, "f %d/%d %d/%d %d/%d\n", a + 1, na + 1, b + 1, nb + 1, c + 1, nc + 1);
    }

    fprintf(stderr, "Processed %d tri smooth [%d]\n", md.ntri_smooth, offsetVertices);
}

void init_triangles_smooth_uv(unsigned int start)
{
    unsigned int nElements = md.ntri_smooth_uv;

    if (nElements)
    {
        unsigned int offsetNormals = start + nElements * sizeof(uint32_t) * 3;
        unsigned int offsetUvs = offsetNormals + nElements * sizeof(uint32_t) * 3;
        unsigned int offsetMaterials = offsetUvs + nElements * sizeof(uint32_t) * 3;

        for (unsigned int i = 0; i < nElements; i++)
        {
            uint32_t a = parseUInt32(data, start + (i * 3) * sizeof(uint32_t));
            uint32_t b = parseUInt32(data, start + (i * 3 + 1) * sizeof(uint32_t));
            uint32_t c = parseUInt32(data, start + (i * 3 + 2) * sizeof(uint32_t));

            uint32_t na = parseUInt32(data, offsetNormals + (i * 3) * sizeof(uint32_t));
            uint32_t nb = parseUInt32(data, offsetNormals + (i * 3 + 1) * sizeof(uint32_t));
            uint32_t nc = parseUInt32(data, offsetNormals + (i * 3 + 2) * sizeof(uint32_t));

            uint32_t ua = parseUInt32(data, offsetUvs + (i * 3) * sizeof(uint32_t));
            uint32_t ub = parseUInt32(data, offsetUvs + (i * 3 + 1) * sizeof(uint32_t));
            uint32_t uc = parseUInt32(data, offsetUvs + (i * 3 + 2) * sizeof(uint32_t));

            uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
            use_material(m);

            // TODO :
            // fprintf(tp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", a + 1, ua + 1, na + 1, b + 1, ub + 1, nb + 1, c + 1, uc + 1, nc + 1);
            fprintf(tp, "f %d/%d %d/%d %d/%d\n", a + 1, ua + 1, b + 1, ub + 1, c + 1, uc + 1);
        }

        fprintf(stderr, "Processed %d tri smooth [%d]\n", md.ntri_smooth, start);
    }
}

void init_faces4_flat(unsigned int nElements, unsigned int offsetVertices, unsigned int offsetMaterials)
{
    for (unsigned int i = 0; i < nElements; i++)
    {
        uint32_t a = parseUInt32(data, offsetVertices + (i * 4) * sizeof(uint32_t));
        uint32_t b = parseUInt32(data, offsetVertices + (i * 4 + 1) * sizeof(uint32_t));
        uint32_t c = parseUInt32(data, offsetVertices + (i * 4 + 2) * sizeof(uint32_t));
        uint32_t d = parseUInt32(data, offsetVertices + (i * 4 + 3) * sizeof(uint32_t));

        uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
        use_material(m);

        fprintf(tp, "f %d %d %d %d\n", a + 1, b + 1, c + 1, d + 1);
    }
}

void init_uvs4(unsigned int nElements, unsigned int offset, unsigned int offsetMaterials)
{
    for (unsigned int i = 0; i < nElements; i++)
    {
        uint32_t a = parseUInt32(data, offset + (i * 4) * sizeof(uint32_t));
        uint32_t b = parseUInt32(data, offset + (i * 4 + 1) * sizeof(uint32_t));
        uint32_t c = parseUInt32(data, offset + (i * 4 + 2) * sizeof(uint32_t));
        uint32_t d = parseUInt32(data, offset + (i * 4 + 3) * sizeof(uint32_t));

        uint32_t ua = parseUInt32(data, offset + (16 * md.nquad_flat_uv) + (i * 16) + 0);
        uint32_t ub = parseUInt32(data, offset + (16 * md.nquad_flat_uv) + (i * 16) + 4);
        uint32_t uc = parseUInt32(data, offset + (16 * md.nquad_flat_uv) + (i * 16) + 8);
        uint32_t ud = parseUInt32(data, offset + (16 * md.nquad_flat_uv) + (i * 16) + 12);

        uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
        use_material(m);

        fprintf(tp, "f %d/%d %d/%d %d/%d %d/%d\n", a + 1, ua + 1, b + 1, ub + 1, c + 1, uc + 1, d + 1, ud + 1);
    }

    fprintf(stderr, "Processed %d quad flat uvs [%d]\n", md.nquad_flat_uv, offset);
}

void init_quads_flat_uv(unsigned int start)
{
    unsigned int nElements = md.nquad_flat_uv;
    if (nElements)
    {
        unsigned int offsetUvs = start + nElements * sizeof(uint32_t) * 4;
        unsigned int offsetMaterials = offsetUvs + nElements * sizeof(uint32_t) * 4;

        init_faces4_flat(nElements, start, offsetMaterials);
        init_uvs4(nElements, offsetUvs, offsetMaterials);
    }
}

void init_faces4_smooth(unsigned int nElements, unsigned int offsetVertices, unsigned int offsetNormals, unsigned int offsetUvs, unsigned int offsetMaterials)
{
    for (unsigned int i = 0; i < nElements; i++)
    {
        uint32_t a = parseUInt32(data, offsetVertices + (i * 4) * sizeof(uint32_t));
        uint32_t b = parseUInt32(data, offsetVertices + (i * 4 + 1) * sizeof(uint32_t));
        uint32_t c = parseUInt32(data, offsetVertices + (i * 4 + 2) * sizeof(uint32_t));
        uint32_t d = parseUInt32(data, offsetVertices + (i * 4 + 3) * sizeof(uint32_t));

        uint32_t na = parseUInt32(data, offsetNormals + (i * 4) * sizeof(uint32_t));
        uint32_t nb = parseUInt32(data, offsetNormals + (i * 4 + 1) * sizeof(uint32_t));
        uint32_t nc = parseUInt32(data, offsetNormals + (i * 4 + 2) * sizeof(uint32_t));
        uint32_t nd = parseUInt32(data, offsetNormals + (i * 4 + 3) * sizeof(uint32_t));

        uint32_t ua = parseUInt32(data, offsetUvs + (i * 4) * sizeof(uint32_t));
        uint32_t ub = parseUInt32(data, offsetUvs + (i * 4 + 1) * sizeof(uint32_t));
        uint32_t uc = parseUInt32(data, offsetUvs + (i * 4 + 2) * sizeof(uint32_t));
        uint32_t ud = parseUInt32(data, offsetUvs + (i * 4 + 3) * sizeof(uint32_t));

        uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
        use_material(m);

        fprintf(tp, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n", a + 1, ua + 1, na + 1, b + 1, ub + 1, nb + 1, c + 1, uc + 1, nc + 1, d + 1, ud + 1, nd + 1);
    }

    fprintf(stderr, "Processed %d quad smooth uvs [%d]\n", md.nquad_smooth_uv, offsetVertices);
}

void init_quads_smooth_uv(unsigned int start)
{
    unsigned int nElements = md.nquad_smooth_uv;

    if (nElements)
    {
        unsigned int offsetNormals = start + nElements * sizeof(uint32_t) * 4;
        unsigned int offsetUvs = offsetNormals + nElements * sizeof(uint32_t) * 4;
        unsigned int offsetMaterials = offsetUvs + nElements * sizeof(uint32_t) * 4;

        for (unsigned int i = 0; i < nElements; i++)
        {
            uint32_t a = parseUInt32(data, start + (i * 4) * sizeof(uint32_t));
            uint32_t b = parseUInt32(data, start + (i * 4 + 1) * sizeof(uint32_t));
            uint32_t c = parseUInt32(data, start + (i * 4 + 2) * sizeof(uint32_t));
            uint32_t d = parseUInt32(data, start + (i * 4 + 3) * sizeof(uint32_t));

            uint32_t na = parseUInt32(data, offsetNormals + (i * 4) * sizeof(uint32_t));
            uint32_t nb = parseUInt32(data, offsetNormals + (i * 4 + 1) * sizeof(uint32_t));
            uint32_t nc = parseUInt32(data, offsetNormals + (i * 4 + 2) * sizeof(uint32_t));
            uint32_t nd = parseUInt32(data, offsetNormals + (i * 4 + 3) * sizeof(uint32_t));

            uint32_t ua = parseUInt32(data, offsetUvs + (i * 4) * sizeof(uint32_t));
            uint32_t ub = parseUInt32(data, offsetUvs + (i * 4 + 1) * sizeof(uint32_t));
            uint32_t uc = parseUInt32(data, offsetUvs + (i * 4 + 2) * sizeof(uint32_t));
            uint32_t ud = parseUInt32(data, offsetUvs + (i * 4 + 3) * sizeof(uint32_t));

            uint16_t m = parseUShort16(data, offsetMaterials + i * sizeof(uint16_t));
            use_material(m);

            // TODO :
            // fprintf(tp, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n", a + 1, ua + 1, na + 1, b + 1, ub + 1, nb + 1, c + 1, uc + 1, nc + 1, d + 1, ud + 1, nd + 1);
            fprintf(tp, "f %d/%d %d/%d %d/%d %d/%d\n", a + 1, ua + 1, b + 1, ub + 1, c + 1, uc + 1, d + 1, ud + 1);
        }

        fprintf(stderr, "Processed %d quad smooth uvs [%d]\n", md.nquad_smooth_uv, start);
    }
}

void init_triangles_flat(unsigned int start)
{
    unsigned int nElements = md.ntri_flat;

    if (nElements > 0)
    {
        unsigned int offsetMaterials = start + nElements * sizeof(uint32_t) * 3;
        init_faces3_flat(nElements, start, offsetMaterials);
    }
}

void init_triangles_smooth(unsigned int start)
{
    unsigned int nElements = md.ntri_smooth;

    if (nElements)
    {
        unsigned int offsetNormals = start + nElements * sizeof(uint32_t) * 3;
        unsigned int offsetMaterials = offsetNormals + nElements * sizeof(uint32_t) * 3;

        init_faces3_smooth(nElements, start, offsetNormals, offsetMaterials);
    }
}

void init_quads_flat(unsigned int start)
{
    unsigned int nElements = md.nquad_flat;

    if (nElements)
    {
        unsigned int offsetMaterials = start + nElements * sizeof(uint32_t) * 4;
        init_faces4_flat(nElements, start, offsetMaterials);
    }
}

void init_quads_smooth(unsigned int start)
{
    unsigned int nElements = md.nquad_smooth;

    if (nElements > 0)
    {
        unsigned int offsetNormals = start + nElements * sizeof(uint32_t) * 4;
        unsigned int offsetUvs = offsetNormals + nElements * sizeof(uint32_t) * 4;
        unsigned int offsetMaterials = offsetNormals + nElements * sizeof(uint32_t) * 4;

        init_faces4_smooth(nElements, start, offsetNormals, offsetUvs, offsetMaterials);
    }
}

int bin2obj(JsonFile jsonFile, const char *binPath, const char *objPath)
{
    size_t flen;
    uint32_t i;
    uint32_t vt;
    struct stat st;

    ::jsonFile = jsonFile;

    if (stat(binPath, &st) != 0)
    {
        fprintf(stderr, "Unable to check file '%s'\n", binPath);
        return 1;
    }

    data = (unsigned char *)malloc(st.st_size);

    if (data == NULL)
    {
        fprintf(stderr, "Unable to allocate %ld bytes for data\n", st.st_size);
        return 1;
    }

    fp = fopen(binPath, "rb");
    if (fp != NULL)
    {
        flen = fread(data, 1, st.st_size, fp);
        fclose(fp);
    }
    else
    {
        fprintf(stderr, "Unable to open .bin file\n");
        free(data);
        return 2;
    }

    fprintf(stderr, "File size : %ld\n", flen);

    tp = fopen(objPath, "wb");
    if (tp == NULL)
    {
        fprintf(stderr, "Unable to open .obj file\n");
        return 2;
    }

    unsigned int currentOffset = 0;
    md = parseMetaData(data, currentOffset);
    currentOffset += md.header_bytes;

    // buffers sizes

    unsigned int tri_size = md.vertex_index_bytes * 3 + md.material_index_bytes;
    unsigned int quad_size = md.vertex_index_bytes * 4 + md.material_index_bytes;

    unsigned int len_tri_flat = md.ntri_flat * (tri_size);
    unsigned int len_tri_smooth = md.ntri_smooth * (tri_size + md.normal_index_bytes * 3);
    unsigned int len_tri_flat_uv = md.ntri_flat_uv * (tri_size + md.uv_index_bytes * 3);
    unsigned int len_tri_smooth_uv = md.ntri_smooth_uv * (tri_size + md.normal_index_bytes * 3 + md.uv_index_bytes * 3);

    unsigned int len_quad_flat = md.nquad_flat * (quad_size);
    unsigned int len_quad_smooth = md.nquad_smooth * (quad_size + md.normal_index_bytes * 4);
    unsigned int len_quad_flat_uv = md.nquad_flat_uv * (quad_size + md.uv_index_bytes * 4);
    unsigned int len_quad_smooth_uv = md.nquad_smooth_uv * (quad_size + md.normal_index_bytes * 4 + md.uv_index_bytes * 4);

    // header
    boost::filesystem::path mtlName = jsonFile.Buffers;
    mtlName.replace_extension(".mtl");
    fprintf(tp, "mtllib %s\n", mtlName.string().c_str());

    // read buffers
    currentOffset += init_vertices(currentOffset);

    currentOffset += init_normals(currentOffset);
    currentOffset += handlePadding(md.nnormals * 3);

    currentOffset += init_uvs(currentOffset);

    unsigned int start_tri_flat = currentOffset;
    unsigned int start_tri_smooth = start_tri_flat + len_tri_flat + handlePadding(md.ntri_flat * 2);
    unsigned int start_tri_flat_uv = start_tri_smooth + len_tri_smooth + handlePadding(md.ntri_smooth * 2);
    unsigned int start_tri_smooth_uv = start_tri_flat_uv + len_tri_flat_uv + handlePadding(md.ntri_flat_uv * 2);

    unsigned int start_quad_flat = start_tri_smooth_uv + len_tri_smooth_uv + handlePadding(md.ntri_smooth_uv * 2);
    unsigned int start_quad_smooth = start_quad_flat + len_quad_flat + handlePadding(md.nquad_flat * 2);
    unsigned int start_quad_flat_uv = start_quad_smooth + len_quad_smooth + handlePadding(md.nquad_smooth * 2);
    unsigned int start_quad_smooth_uv = start_quad_flat_uv + len_quad_flat_uv + handlePadding(md.nquad_flat_uv * 2);

    // have to first process faces with uvs
    // so that face and uv indices match

    init_triangles_flat_uv(start_tri_flat_uv);
    init_triangles_smooth_uv(start_tri_smooth_uv);

    init_quads_flat_uv(start_quad_flat_uv);
    init_quads_smooth_uv(start_quad_smooth_uv);

    // // now we can process untextured faces

    // init_triangles_flat(start_tri_flat);
    // init_triangles_smooth(start_tri_smooth);

    // init_quads_flat(start_quad_flat);
    // init_quads_smooth(start_quad_smooth);

    fprintf(tp, "\n");

    fclose(tp);

    free(data);
    return 0;
}
